using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using System.Net.Sockets;

namespace CVS
{
    public class Client : MarshalByRefObject
    {
        private delegate IRepositorio CheckoutRepositoryDelegate( int id, int contador );
        private CheckoutRepositoryDelegate _checkoutRepositoryDelegate;

        private delegate int CheckinRepositoryDelegate( IRepositorio rep, int contador );
        private CheckinRepositoryDelegate _checkinRepositoryDelegate;

        private delegate bool DeleteRepositoryDelegate( string repId, int contador );
        private DeleteRepositoryDelegate _deleteRepositoryDelegate;

        private delegate IRepositorio CreateRepositoryDelegate( string repId );
        private CreateRepositoryDelegate _createRepositoryDelegate;

        private delegate string ListFilesDelegate( string repId, int contador );
        private ListFilesDelegate _listFilesDelegate;

        private delegate int DeleteFileDelegate( string repId, string name, int contador );
        private DeleteFileDelegate _deleteFileDelegate;

        TcpChannel _channel;
        IRemoteServer _obj = null;
        IGestorRepositorios _manager = new GestorRepositorios();
        private string _ipAddress;
        private string _serverAddress;

        public string IpAddress
        {
            get { return _ipAddress; }
            set { _ipAddress = value; }
        }

        public Client(TcpChannel channel, string address)
        {
            _channel = channel;
            IpAddress = address;
        }

        public bool Connect(string address)
        {
            _serverAddress = address;

            _obj = (IRemoteServer)Activator.GetObject(
                typeof(IRemoteServer),
                "tcp://"+ _serverAddress +"/MyRemoteServer");

            return true;
        }

        public IRepositorio CheckoutRepository(string id)
        {
            this._checkoutRepositoryDelegate = new CheckoutRepositoryDelegate( _obj.Checkout );

            IAsyncResult tag = this._checkoutRepositoryDelegate.BeginInvoke( Int32.Parse(id),0, null, null );
            IRepositorio rep = this._checkoutRepositoryDelegate.EndInvoke( tag );

            //If no repository with the given id was found on the server, return null
            if (rep == null)
            {
                MessageBox.Show("Check-out failed: no such id.");
                return null;
            }

            IRepositorio temp;
            //If the repository already exists, remove it from the list
            if ((temp = _manager.ObtemRepositorio(rep.Id)) != null)
            {
                if (rep.Versao > temp.Versao)
                    _manager.Repositorios.Remove(temp);
                else
                {
                    MessageBox.Show("The repository is not updated.");
                    return null;
                }
            }

            rep.Alterado = false;

            //Add the repository to the list
            if (rep.Versao > temp.Versao)
                _manager.Repositorios.Add(rep);
            return rep;
        }

        public Boolean CheckInRepository(string rep_number)
        {
            IRepositorio rep = _manager.ObtemRepositorio(rep_number);

            this._checkinRepositoryDelegate = new CheckinRepositoryDelegate( _obj.Checkin );

            IAsyncResult tag = this._checkinRepositoryDelegate.BeginInvoke( rep, 0, null, null );
            int returnValue = this._checkinRepositoryDelegate.EndInvoke( tag );

            rep.Versao++;
            if (returnValue == -1)
            {
                //Update repository state
                rep.Alterado = false;

                foreach (IFicheiro file in rep.Ficheiros)
                {
                    file.Alterado = false;
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        public Boolean DeleteRepository(string rep_id)
        {
            IRepositorio rep = _manager.ObtemRepositorio(rep_id);

            this._deleteRepositoryDelegate = new DeleteRepositoryDelegate( _obj.DeleteRep );

            IAsyncResult tag = this._deleteRepositoryDelegate.BeginInvoke( rep_id, 0, null, null );
            bool result = this._deleteRepositoryDelegate.EndInvoke( tag );

            if (result)
            {
                _manager.Repositorios.Remove(rep);
            }

            return true;
        }    

        public IRepositorio CreateRepository(string name)
        {
            this._createRepositoryDelegate = new CreateRepositoryDelegate( _obj.CreateRep );

            IAsyncResult tag = this._createRepositoryDelegate.BeginInvoke( name, null, null );
            IRepositorio rep = this._createRepositoryDelegate.EndInvoke( tag );

            rep.Alterado = true;

            //Add a local copy of the repository to the client list
            _manager.Repositorios.Add(rep);

            return rep;
        }

        public string ListFiles(string rep_number)
        {
            this._checkoutRepositoryDelegate = new CheckoutRepositoryDelegate( _obj.Checkout );

            IAsyncResult tag = this._checkoutRepositoryDelegate.BeginInvoke( Int32.Parse( rep_number ), 0, null, null );
            IRepositorio rep = this._checkoutRepositoryDelegate.EndInvoke( tag );

            if ( !( _manager.Repositorios ).Contains( rep ) )
                _manager.Repositorios.Add( rep );

            this._listFilesDelegate = new ListFilesDelegate( _obj.ListFiles );

            tag = this._listFilesDelegate.BeginInvoke( rep_number, 0, null, null );
            string result = this._listFilesDelegate.EndInvoke( tag );
            
            return result;
        }

        public List<IFicheiro> ShowRepContent(string id)
        {
            return _manager.ObtemRepositorio(id).Ficheiros;
        }

        public void CreateFile(string name, string rep_number)
        {
            IRepositorio rep = _manager.ObtemRepositorio(rep_number);

            //Create the new file
            IFicheiro file = new FicheiroBase();
            file.Nome = name;
            file.Conteudo = "Ficheiro vazio";
            file.Alterado = true;

            //Add the new file to the current repository file list
            rep.Ficheiros.Add(file);
        }

        public string ShowFileContent(string rep_number, string filename)
        {
            IRepositorio rep = _manager.ObtemRepositorio(rep_number);
            return rep.ObtemFicheiro(filename).Conteudo;
        }

        public bool SaveFile(string rep_number, string filename, string content)
        {
            IRepositorio rep = _manager.ObtemRepositorio(rep_number);
            IFicheiro file = rep.ObtemFicheiro(filename);

            //If file content has changed, save the new content and return true
            if (file.Conteudo.CompareTo(content) != 0)
            {
                file.Conteudo = content;
                file.Alterado = true;
                return true;
            }
            //Otherwise do nothing and return false
            else
            {
                return false;
            }
        }

        public void DeleteFile(string rep_id, string file_name, int previousOperation)
        {
            if(previousOperation==1) 
            {
                IRepositorio rep = _manager.ObtemRepositorio(rep_id);
                //previousOperation = check-out
                foreach (IFicheiro file in rep.Ficheiros)
                {
                    if (file.Nome.CompareTo(file_name) == 0)
                    {
                        rep.Ficheiros.Remove(file);
                        break;
                    }
                }
            }
            else {
                this._deleteFileDelegate = new DeleteFileDelegate( _obj.DeleteFile );

                IAsyncResult tag = this._deleteFileDelegate.BeginInvoke( rep_id, file_name,0, null, null );
                int result = this._deleteFileDelegate.EndInvoke( tag );

                //previousOperation = lista_ficheiros
                switch (result) { 
                            case -1:
                                MessageBox.Show("Unable to delete file -> Unknown Repository");
                                break;
                            case 0:
                                MessageBox.Show("Unable to delete file -> Unknown File");
                                break;
                            case 1:
                                MessageBox.Show("File Deleted");
                                break;
                            default:
                                MessageBox.Show("Unable to delete file -> Unknown Error");
                                break;
                }
            }
        }
    }
}
